#include <iostream>
#include <include/graph.hpp>
#include <include/dilatation.hpp>
#include <include/graph_init.hpp>
#include <include/permutation_utils.hpp>
#include <include/distributions.hpp>
#include <include/distribution_utils.hpp>
#include <include/graph_utils.hpp>
#include <mpi.h>

int main(int argc, char* argv[]) {
    int myRank;
    int countOfProcesses;

    /* start up MPI */
    MPI_Init( &argc, &argv );

    /* find out process rank */
    MPI_Comm_rank(MPI_COMM_WORLD, &myRank);

    /* find out number of processes */
    MPI_Comm_size(MPI_COMM_WORLD, &countOfProcesses);

    //control file parameter
    string file;

    if(argc > 2 || argc == 1) {
         std::cerr << "Error: please write name of paramter as an program argument.";
    }
    else {
        file = argv[1];
    }

    Graph * graph = new Graph();
    Dilatation * dilatation = new Dilatation(graph);

    GraphInit * initialization = new GraphInit();

    initialization->graphLoad(file, graph);

    dilatation->buildHashmap();
    
    int graphsDiameter = GraphUtils::computeGraphsDiameter(graph);
    int numberOfVertices = graph->getNumberOfVertices();
    graph->setLowerBound(ceil((float) (numberOfVertices - 1) / (float)graphsDiameter));
    int lowerBound = graph->getLowerBound();
    
    int LENGTH = 100;
    int tag=1;
    MPI_Status status;
    //char buffer[LENGTH];
    int position=1;
    int message[LENGTH];
    MPI_Request request;
    //here we will send messages to another CPUs about distributions
    
    double t1, t2;
    MPI_Barrier(MPI_COMM_WORLD); /* cekam na spusteni vsech procesu */

    t1=MPI_Wtime(); /* pocatecni cas */
  
    if (myRank == 0) {
	printf("Graphs info:\n");
	std::cout << "\tFile name: " << argv[1] << std::endl;
	printf("\tDiameter: %d\n",graphsDiameter);
	printf("\tLower bound: %d\n", lowerBound);
	
	std::vector<int> cpuIds;
	for(int i = 0; i < countOfProcesses; ++i) {
	    cpuIds.push_back(i);
	}

	std::vector<Distributions> distributions;
	distributions = DistributionUtils::computeDistributions(cpuIds,1,0,PermutationUtils::factorial(numberOfVertices),numberOfVertices);

	typedef std::vector<Distributions>::iterator iter_vec_distr;
	for(iter_vec_distr iter = distributions.begin(); iter != distributions.end(); ++iter) {
	    std::cout << "CPU ID: " << (*iter).cpuId << " Init perm number: " << (*iter).initialPermNumber << " Perms to process: " << (*iter).countOfPemrsToProcess << std::endl;
	}

        /* create messages */
	printf("\nThere are %d processes.\n", countOfProcesses);
	printf("Cpu with rank 0 has distribution: %d %d\n",distributions.at(0).initialPermNumber, distributions.at(0).countOfPemrsToProcess);
        for (int target=1;target<countOfProcesses;++target) {
	    message[0]=distributions.at(target).initialPermNumber;
	    message[1]=distributions.at(target).countOfPemrsToProcess;
	    std::cout << "Sending initPermNum: " << distributions.at(target).initialPermNumber << ", permsToProcess: " << distributions.at(target).countOfPemrsToProcess << ", to CPU ID: " << target << std::endl;
	    MPI_Send (message, 2, MPI_INT, target, tag, MPI_COMM_WORLD);
	}
	//here cpu with rank 0 will begin compute
	Distributions distr;
        distr.initialPermNumber = distributions.at(0).initialPermNumber;
        distr.countOfPemrsToProcess = distributions.at(0).countOfPemrsToProcess;
	std::cout << "CPU  with ID: " << myRank << ", DILATATION: " << dilatation->getDilatation(distr) << std::endl;
    }
    else {
        position=0;
	int initPermNum;
	int countOfPermsToProcess;

	MPI_Recv(message, LENGTH, MPI_INT, MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &status);
	printf("CPU with id: %d has received a message array: Message[0]: %d, message[1]: %d\n", myRank, message[0],message[1]);
	Distributions distr;
        distr.initialPermNumber = message[0];
        distr.countOfPemrsToProcess = message[1];
        std::cout << "CPU  with ID: " << myRank << ", DILATATION: " << dilatation->getDilatation(distr) << std::endl;
    }

    //while(true) {
    //}
    
    //MPI_Barrier(MPI_COMM_WORLD); /* cekam na dokonceni vypoctu */

    t2=MPI_Wtime(); /* koncovy cas */

    printf("Spotrebovany cas je %f.\n",t2-t1);

    MPI_Barrier(MPI_COMM_WORLD);
    std::cout << "finalizing cpu id: " << myRank << std::endl;
    MPI_Finalize();
    std::cout << "finalized cpu id: " << myRank << std::endl;

    return 0;
}
